package com.xd.springboot.service.impl;

import com.xd.springboot.dao.UserMapper;
import com.xd.springboot.entity.User;
import com.xd.springboot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.SQLException;
import java.util.List;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public User getUserByName(String name) {
        return userMapper.getUserByName(name);
    }

    @Override
    public User getUser(Long id) {
        return userMapper.getUser(id);
    }

    @Override
    public User getUser(Long id, String name) {
        return userMapper.getUserByIdAndName(id,name);
    }

    @Override
    public List<User> getAll() {
        return userMapper.getAll();
    }

    @Override
    @Transactional
    public void insertUser(User user) {
        userMapper.insertUser(user);
        //遇到运行异常（RuntimeException）和程序错误（Error）才会回滚
        throw new RuntimeException();
    }


    //--------------------------------事务在实际开发中会遇到的3个坑----------------------------------
    //1. 异常没有被捕获
    //2. 异常被try吃掉
    //3. 事务的范围比锁的范围大

    @Override
    @Transactional(rollbackFor = Exception.class)  //
    public void insertUser2(User user) throws Exception {
        userMapper.insertUser(user);
        //数据库异常必须指定回滚，否则发生异常也不会回滚
        throw new SQLException("数据库异常");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertUser3(User user) throws Exception {
        try {
            userMapper.insertUser(user);
            //异常被处理了，并没有抛出，这样也不会导致回滚
            throw new SQLException("数据库异常");
        } catch (Exception e){
            //异常处理逻辑
        }
    }

    //synchronized 同步，用于多线程，一次只有一个线程进入该方法
    //事务的范围 比 锁的范围大
    //加锁的代码执行完成之后，锁被释放掉了，但是事务还没有结束，此时另一个线程进入
    //此时事务还没有结束，数据库的状态和第一个线程刚进来是一样的，mysql innodb默认的
    //隔离级别是可重复读，在同一个事务中，selet的结果是事务开始时时间点的状态，线程2开始的时候
    //线程1还没有提交完成，导致读取的数据还有更新，第二个线程也做了插入动作，导致了脏数据
    // 解决办法，在调用该service的地方加锁，保证锁的范围要比事务的范围大即可
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized void insertUser4(User user) throws Exception {
        //具体的业务
        userMapper.insertUser(user);
    }
}
